package uk.co.droidinactu.inlibrislibertas.model;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;

import uk.co.droidinactu.inlibrislibertas.LibraryConstants;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;

public class EBookImporter {

	private static final String LOG_TAG = "InLibris_EBookImporter";

	private final CalibreDataModel dataMdl;

	private final DbMetadata dbMetaData;
	private final List<String> ebookDirList = new ArrayList<String>();

	public EBookImporter(final CalibreDataModel dataModel, final DbMetadata dbMetaDta) {
		dataMdl = dataModel;
		dbMetaData = dbMetaDta;
		ebookDirList.add("/sdcard/ebooks");
		ebookDirList.add("/sdcard/EBooks");
		ebookDirList.add("/sdcard/eBooks");
	}

	public void addEbookDir(final String dirName) {
		ebookDirList.add(dirName);
	}

	/**
	 * This method checks for the existence of the calibre metadata file.
	 * 
	 * @param calibreFilename
	 * @return 3 if an unknown error occurred while we looked for the file; 2 if
	 *         the file does not exist; 1 if the file exists, but we cannot read
	 *         it; 0 if the file exists and we can read it (okay to use)
	 */
	private int checkCalibreMetadataFile(final String calibreFilename) {
		int retValue = 0;
		final File f = new File(calibreFilename);
		try {
			if (!f.exists()) {
				retValue = 2;
			} else {
				if (!f.canRead()) {
					retValue = 1;
				}
			}
		} catch (final Exception e) {
			retValue = 3;
		}
		return retValue;
	}

	// public List<EBook> filterBookList(final List<FilterEntry> filterTokns) {
	//
	// List<EBook> bkListTmp = new ArrayList<EBook>();
	//
	// if (filterTokns.size() == 0) {
	// bkListTmp = dataMdl.getBooks();
	// } else {
	//
	// final List<EBook> bkLst = dataMdl.getBooks();
	//
	// for (int x = 0; x < bkLst.size(); x++) {
	// final EBook cb = bkLst.get(x);
	// // Go through the filters to see if this book matches
	// boolean orMatch = false;
	// boolean andMatch = true;
	//
	// if (filterTokns.size() == 1) {
	// final FilterEntry fe = filterTokns.get(0);
	// final boolean bkMatchsTkn = cb.matchesFilter(fe);
	//
	// if ((fe.boolOp == BooleanOperator.ANDNOT) || (fe.boolOp ==
	// BooleanOperator.ORNOT)) {
	// if (bkMatchsTkn) {
	// orMatch = false;
	// andMatch = false;
	// }
	// orMatch = true;
	// } else if (fe.boolOp == BooleanOperator.AND) {
	// if (!bkMatchsTkn) {
	// andMatch = false;
	// }
	// } else if (fe.boolOp == BooleanOperator.OR) {
	// if (bkMatchsTkn) {
	// orMatch = true;
	// }
	// }
	// if (andMatch && orMatch) {
	// bkListTmp.add(cb);
	// }
	//
	// } else {
	//
	// for (final FilterEntry fe : filterTokns) {
	// final boolean bkMatchsTkn = cb.matchesFilter(fe);
	//
	// if (fe.boolOp == BooleanOperator.ANDNOT) {
	// if (bkMatchsTkn) {
	// orMatch = false;
	// andMatch = false;
	// break;
	// }
	// } else if (fe.boolOp == BooleanOperator.ORNOT) {
	// if (bkMatchsTkn) {
	// orMatch = false;
	// andMatch = false;
	// break;
	// }
	// orMatch = true;
	// } else if (fe.boolOp == BooleanOperator.AND) {
	// if (!bkMatchsTkn) {
	// andMatch = false;
	// }
	// } else if (fe.boolOp == BooleanOperator.OR) {
	// if (bkMatchsTkn) {
	// orMatch = true;
	// }
	// }
	// }
	// if (andMatch && orMatch) {
	// bkListTmp.add(cb);
	// }
	// }
	// }
	// }
	// return bkListTmp;
	// }

	/*
	 * Maybe consider the Google search syntax? All terms being ANDed for top
	 * matches and ORed for lower ranking matches, with text in quotes being
	 * exact matches, and a dash ("-") before a term meaning "NOT". Maybe not.
	 * Maybe Or/And/AndNot/OrNot would be simpler and clearer...
	 * 
	 * My personal usage of multi-tag searching I think would normally be all
	 * ANDs, e.g. "Sci-Fi AND ToRead".
	 */
	public List<EBook> filterBookListGoogle(final List<FilterEntry> filterTokns) {
		final List<EBook> bkListTmp = new ArrayList<EBook>();

		// FIXME:

		return bkListTmp;
	}

	private void findBooksInDir(final String dirname) {
		final int nextIndexNbr = 0;
		final List<File> files = getFilesInDir(dirname, false);
		for (final File f : files) {
			if (f.getName().endsWith(".epub")) {
				dataMdl.addBook(importEpubFile(dirname + File.separator + f.getName(), nextIndexNbr));
			} else if (f.getName().endsWith(".pdf")) {
				// FIXME bkList.add(importPDFFile(dirname+File.separator+s,
				// nextIndexNbr)) ;
			}
		}

		final List<File> subdirs = getFilesInDir(dirname, true);
		for (final File f : subdirs) {
			findBooksInDir(dirname + File.separator + f.getName());
		}
	}

	private void getCoverImages(final Context ctx, final int noImgId, final int noImgIdEpub, final int noImgIdPdf) {
		final Thread importMetadataThrd = new Thread() {
			@Override
			public void run() {
				final Cursor results = dataMdl.getBooks();
				results.moveToFirst();
				for (int x = 0; x < results.getCount(); x++) {
					final EBook ebk = new EBook(results);
					ebk.getCoverImg(ctx, noImgId, noImgIdEpub, noImgIdPdf);
					results.moveToNext();
				}
				results.close();
			}
		};
		importMetadataThrd.setName("Caching Book Covers");
		importMetadataThrd.start();
	}

	private List<File> getFilesInDir(final String dirname, final boolean isDir) {
		final File dir = new File(dirname);

		// String[] children = dir.list();
		// if (children == null) { // Either dir does not exist or is not a
		// directory
		// }
		// else {
		// for (int i=0; i<children.length; i++) { // Get filename of file or
		// directory
		// String filename = children[i];
		// }
		// } // It is also possible to filter the list of returned files.
		//
		// // This example does not return any files that start with `.'.
		// FilenameFilter filter = new FilenameFilter() {
		// public boolean accept(File dir, String name) {
		// return !name.startsWith(".");
		// }
		// };
		// children = dir.list(filter); // The list of files can also be
		// retrieved as File objects
		// File[] files = dir.listFiles();

		// This filter only returns directories
		final FileFilter fileFilter = new FileFilter() {
			@Override
			public boolean accept(final File file) {
				return isDir && file.isDirectory();
			}
		};
		// files = dir.listFiles(fileFilter);

		final List<File> fileList = new ArrayList<File>();
		for (final File f : dir.listFiles(fileFilter)) {
			fileList.add(f);
		}
		return fileList;
	}

	private String[] getToken(String str) {
		boolean foundquotes = false;
		final String[] retValues = new String[2];
		String retStr = "";
		try {
			if (str.startsWith("\"")) {
				foundquotes = true;
				// we are looking for the next quote symbol
				final int quoteLoc = str.indexOf('\"', 1);
				retStr = str.substring(1, quoteLoc);
			} else {
				final int spaceLoc = str.indexOf(' ');
				if (spaceLoc < 0) {
					retStr = str;
				} else {
					retStr = str.substring(0, spaceLoc);
				}
			}
		} catch (final Exception e) {
			retStr = str;
		}

		if (foundquotes) {
			str = str.substring(retStr.length() + 2);
		} else {
			str = str.substring(retStr.length());
		}

		retValues[0] = retStr.trim();
		retValues[1] = str.trim();

		return retValues;
	}

	public void importBooks(final String metadataFilename) {
		final CalibreMetadataReader cbkRdr = new CalibreMetadataReader(dataMdl);

		final File f = new File(metadataFilename);
		if (f.exists()) {
			if (f.lastModified() == dbMetaData.timestamp) {
				return;
			}

			dataMdl.clearDb();

			dbMetaData.timestamp = f.lastModified();
			dataMdl.updateDbMetaData(dbMetaData);

			cbkRdr.setFilename(metadataFilename);
			try {
				cbkRdr.openFile();
			} catch (final Exception e) {
				Log.e(LibraryConstants.LOG_TAG + EBookImporter.LOG_TAG,
						"Exception opening metadata file : " + e.getMessage());
			}
			try {
				cbkRdr.readBooks();
			} catch (final Exception e) {
				Log.e(LibraryConstants.LOG_TAG + EBookImporter.LOG_TAG, "Exception reading books : " + e.getMessage());
			}
			try {
				cbkRdr.closeFile();
			} catch (final Exception e) {
				Log.e(LibraryConstants.LOG_TAG + EBookImporter.LOG_TAG,
						"Exception closing metadata file : " + e.getMessage());
			}
		} else {
			// Search for books in sub dir's
			for (final String dir : ebookDirList) {
				findBooksInDir(dir);
			}
		}
	}

	private EBook importEpubFile(final String filename, final int nextIndexNbr) {
		final EBook cb = new EBook();
		cb.lpath = filename;
		cb.readMetadata();

		final File f = new File(filename);
		// read in file metadata
		// use tika??

		return cb;
	}

	// private ArrayList<FilterEntry> parseFilter(String filter) {
	// final ArrayList<FilterEntry> filterTokns = new ArrayList<FilterEntry>();
	// String boolOp = "Or";
	//
	// // get first 'token' (whether a word or a quote contained string)
	// String[] tkn = getToken(filter.trim());
	// filter = tkn[1];
	// try {
	// if (BooleanOperatorFactory.isBooleanOperator(tkn[0])) {
	// boolOp = tkn[0];
	// tkn = getToken(filter.trim());
	// filter = tkn[1];
	// }
	// } catch (final StringIndexOutOfBoundsException e) {
	// System.out.println("StringIndexOutOfBoundsException parsing filter : " +
	// e.getMessage());
	// filter = "";
	// } catch (final Exception e) {
	// System.out.println("General Exception parsing filter : " +
	// e.getMessage());
	// filter = "";
	// }
	//
	// filterTokns.add(new
	// FilterEntry(BooleanOperatorFactory.getBooleanOperator(boolOp), tkn[0]));
	//
	// while (filter.length() > 0) {
	// tkn = getToken(filter.trim());
	// filter = tkn[1];
	// if (BooleanOperatorFactory.isBooleanOperator(tkn[0])) {
	// boolOp = tkn[0];
	// tkn = getToken(filter.trim());
	// filter = tkn[1];
	// } else {
	// boolOp = "Or";
	// }
	// filterTokns.add(new
	// FilterEntry(BooleanOperatorFactory.getBooleanOperator(boolOp), tkn[0]));
	// }
	// return filterTokns;
	// }

	private void removeEbookDir(final String dirName) {
		ebookDirList.remove(dirName);
	}

	// private void setFilter(final String filter) {
	// final ArrayList<FilterEntry> fltrTkns = parseFilter(filter);
	// // FIXME : How do we now filter the book list?
	// }

	public void sortBooks(final int sortOrderIndex) {
		// switch (sortOrderIndex) {
		// case 1:
		// Collections.sort(this.bkList, new EBookComparatorAuthorSortTitle());
		// Collections.sort(this.bkListFiltered, new
		// EBookComparatorAuthorSortTitle());
		// break;
		// case 2:
		// Collections.sort(this.bkList, new EBookComparatorSeries());
		// Collections.sort(this.bkListFiltered, new EBookComparatorSeries());
		// break;
		// default:
		// Collections.sort(this.bkList, new EBookComparatorTitle());
		// Collections.sort(this.bkListFiltered, new EBookComparatorTitle());
		// }
	}

}
